package com.proxy.factory;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Better Feng
 * @date 2021/8/30 10:09
 */
public class ProxyFactory<T> {

    private final Class<T> interfaceClass;


    public ProxyFactory(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    public T createProxyInstance() {
        return  (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                } else if (method.isDefault()) {
                    return invokeDefaultMethod(proxy, method, args);
                }
                return "Do something you want to do.";
            }
        });
    }

    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
            throws Throwable {
        Constructor<MethodHandles.Lookup> defaultMethodConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        if (!defaultMethodConstructor.isAccessible()) {
            defaultMethodConstructor.setAccessible(true);
        }
        MethodHandles.Lookup defaultMethodLookup = defaultMethodConstructor.newInstance(interfaceClass, MethodHandles.Lookup.PRIVATE);
        return defaultMethodLookup.unreflectSpecial(method, interfaceClass)
                .bindTo(proxy).invokeWithArguments(args);
    }

}
